/*
 *
 *   Copyright (C) 2005 by Raymond Huang
 *   plushuang at users.sourceforge.net
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  ---
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of portions of this program with the
 *  OpenSSL library under certain conditions as described in each
 *  individual source file, and distribute linked combinations
 *  including the two.
 *  You must obey the GNU Lesser General Public License in all respects
 *  for all of the code used other than OpenSSL.  If you modify
 *  file(s) with this exception, you may extend this exception to your
 *  version of the file(s), but you are not obligated to do so.  If you
 *  do not wish to do so, delete this exception statement from your
 *  version.  If you delete this exception statement from all source
 *  files in the program, then also delete it here.
 *
 */

#include <string.h>
#include <stdlib.h>
#include <glib.h>
#include <urlglib/category_node.h>
#include <urlglib/download_task.h>
#include <urlglib/url_info.h>
#include <urlglib/ug_i18n.h>

// functions for g_list_foreach () -----------------------------------
static void  active_list_foreach_stop (gpointer data, gpointer user_data)
{
	DownloadTask* dtask = data;

	if (dtask->download)
		download_node_stop (dtask->download);
}

static void  active_list_foreach_finalize (gpointer data, gpointer user_data)
{
	DownloadTask* dtask = data;

	if (dtask->download) {
		download_node_stop (dtask->download);
		download_task_unref (dtask);
	}
}

// CategoryNode ------------------------------------------------------
static void category_node_finalize (CategoryNode* cnode);

CategoryNode* category_node_new ()
{
	CategoryNode* cnode = category_node_new_without_queue ();
	QueueNode* qnode;

	// add waiting queue
	qnode = queue_node_new ();
	base_node_append (BASE_NODE (cnode), BASE_NODE (qnode));
	// add completed queue
	qnode = queue_node_new_reverse ();
	base_node_append (BASE_NODE (cnode), BASE_NODE (qnode));
	// add recycled queue
	qnode = queue_node_new_reverse ();
	base_node_append (BASE_NODE (cnode), BASE_NODE (qnode));

	return cnode;
}

CategoryNode* category_node_new_without_queue ()
{
	CategoryNode* cnode = g_malloc0 (sizeof (CategoryNode));

	// initialize base class
	base_node_instance_init (BASE_NODE (cnode));
	cnode->finalize = (BaseNodeFinalizeFunc) category_node_finalize;

//	cnode->name         = NULL;
	cnode->active_max   = 1;
	cnode->completed_capacity = 300;
	cnode->recycled_capacity  = 300;
//	cnode->download_default   = NULL;
	// initialize others
	cnode->active_list = NULL;
	cnode->download_default = download_node_new ();

	return cnode;
}

static void category_node_finalize (CategoryNode* cnode)
{
	if (cnode->download_default)
		download_node_unref (cnode->download_default);

	g_free (cnode->name);
	g_list_foreach (cnode->active_list, active_list_foreach_finalize, NULL);
	g_list_free (cnode->active_list);
	base_node_instance_finalize (BASE_NODE (cnode));
}

gboolean category_node_activate_download (CategoryNode* cnode, DownloadNode* dnode)
{
	BaseNode* download_parent = base_node_parent (BASE_NODE (dnode));
	BaseNode* category_child  = base_node_child (BASE_NODE (cnode));
	DownloadTask* dtask = NULL;
	UrlInfo   uinfo;

	if (download_parent == category_child) {
		url_info_part (&uinfo, dnode->url);
		if (uinfo.protocol) {
			dtask = download_task_new_from_protocol (uinfo.protocol, uinfo.protocol_len);
			if (dtask) {
				cnode->active_list = g_list_prepend (cnode->active_list, dtask);
				download_task_set (dtask, dnode, NULL);
				download_task_activate (dtask);
			} else {
				dnode->state = DOWNLOAD_STATE_ERROR;
				download_node_set_message (dnode, _("Protocol not support (no plug-in support it)."));
			}
		}
	}
	else
		g_message ("category_node_activate_download () : download_parent != category_child.");

	return (dtask) ? TRUE : FALSE;
}

void  category_node_refresh (CategoryNode* cnode)
{
	DownloadTask* dtask;
	DownloadNode* dnode;
	QueueNode* waiting_queue   = category_node_waiting_queue (cnode);
	QueueNode* completed_queue = category_node_completed_queue (cnode);
	QueueNode* recycled_queue  = category_node_recycled_queue (cnode);
	GList* list;
	GList* list_next;

	// refresh
	for (list=cnode->active_list; list; list=list_next) {
		list_next = list->next;
		dtask = list->data;
		dnode = dtask->download;

		// if dnode removed by user
		if (base_node_parent (BASE_NODE (dnode)) == NULL) {
			cnode->active_list = g_list_remove_link (cnode->active_list, list);
			g_list_free_1 (list);
			download_task_unref (dtask);
			continue;
		}

		if (dnode->sync_code) {
			queue_node_changed (waiting_queue, dnode);
			dnode->sync_code = 0;
		}
		if (dtask->thread == NULL) {
			cnode->active_list = g_list_remove_link (cnode->active_list, list);
			g_list_free_1 (list);
			if (dnode->state == DOWNLOAD_STATE_COMPLETED) {
				download_node_unlink (dnode);
				queue_node_prepend (completed_queue, dnode);
			}
			download_task_unref (dtask);
		}
	}

	// clear excess DownloadNode
	queue_node_remove_after_nth (completed_queue, cnode->completed_capacity);
	queue_node_remove_after_nth (recycled_queue,  cnode->recycled_capacity);
}

int  category_node_start (CategoryNode* cnode)
{
//	DownloadTask* dtask;
	DownloadNode* dnode;
	QueueNode* waiting_queue   = category_node_waiting_queue (cnode);
//	QueueNode* completed_queue = category_node_completed_queue (cnode);
//	QueueNode* recycled_queue  = category_node_recycled_queue (cnode);
//	GList* list;
//	GList* list_next;

	// refresh
//	category_node_refresh (cnode);

	// find waiting download and start it.
	dnode = NULL;
	while (cnode->active_max > category_node_n_active_download (cnode)) {
		dnode = queue_node_find (waiting_queue, DOWNLOAD_STATE_WAITING, dnode);
		if (dnode == NULL)
			break;
		category_node_activate_download (cnode, dnode);
	}

	return category_node_is_active (cnode);
}

void category_node_stop (CategoryNode* cnode)
{
	g_list_foreach (cnode->active_list, active_list_foreach_stop, NULL);
}

// config writer & parser --------------------------------------------

void category_node_write_conf (CategoryNode* cnode, ConfWriter* cw)
{
	QueueNode* qnode;

	conf_writer_start_element (cw, CATEGORY_NODE_TAG
	         " name=\"%s\""
			 " active_max=\"%d\""
	         " completed_capacity=\"%d\""
	         " recycled_capacity=\"%d\"",
	         (cnode->name) ? cnode->name : "",
	         cnode->active_max,
	         cnode->completed_capacity,
	         cnode->recycled_capacity
	         );
	if (cnode->download_default)
		download_node_write_conf (cnode->download_default, cw);

	// write queue data
	qnode = category_node_waiting_queue (cnode);
	if (qnode)
		queue_node_write_conf (qnode, cw);

	qnode = category_node_completed_queue (cnode);
	if (qnode)
		queue_node_write_conf (qnode, cw);

	qnode = category_node_recycled_queue (cnode);
	if (qnode)
		queue_node_write_conf (qnode, cw);

	conf_writer_end_element (cw, CATEGORY_NODE_TAG);
}

// parser ---

void category_node_parser_attr (const gchar**     attr_names,
                                const gchar**     attr_values,
                                CategoryNode*     cnode)
{
	int ii;

	for (ii=0; attr_names[ii]; ii++) {
		if (strcmp (attr_names[ii], "name")==0)
			str_replace_no_crlf (&cnode->name, attr_values[ii], -1);
		else if (strcmp (attr_names[ii], "active_max")==0)
			cnode->active_max = atoi (attr_values[ii]);
		else if (strcmp (attr_names[ii], "completed_capacity")==0)
			cnode->completed_capacity = atoi (attr_values[ii]);
		else if (strcmp (attr_names[ii], "recycled_capacity")==0)
			cnode->recycled_capacity = atoi (attr_values[ii]);
	}
}

void category_node_parser_start_element (GMarkupParseContext* gmpc,
                                         const gchar*    element_name,
                                         const gchar**   attr_names,
                                         const gchar**   attr_values,
                                         gpointer        cparser,
                                         GError**        error)
{
	CategoryNode*  cnode = CONF_PARSER_DATA (cparser);
	QueueNode*     qnode;
	int  qnode_type;

	if (strcmp (element_name, DOWNLOAD_NODE_TAG)==0 && cnode->download_default) {
		conf_parser_push (cparser, &download_node_parser , cnode->download_default);
		download_node_parser_attr (attr_names, attr_values, cnode->download_default);
	} else if (strcmp (element_name, QUEUE_NODE_TAG)==0) {
		qnode_type = queue_node_parser_attr (attr_names, attr_values);
		switch (qnode_type) {
		case 0:
			qnode = category_node_waiting_queue (cnode);
			break;
		case 1:
			qnode = category_node_completed_queue (cnode);
			break;
		case 2:
			qnode = category_node_recycled_queue (cnode);
			break;
		}
		if (qnode)
			conf_parser_push (cparser, &queue_node_parser, qnode);
	} else if (strcmp (element_name, CATEGORY_NODE_TAG)==0) {
		category_node_parser_attr (attr_names, attr_values, cnode);
	}
}

void category_node_parser_end_element (GMarkupParseContext*  gmpc,
                                       const gchar*      element_name,
                                       gpointer          cparser,
                                       GError**          error)
{
	if (strcmp (element_name, CATEGORY_NODE_TAG)==0)
		conf_parser_pop (cparser);
}

const GMarkupParser category_node_parser = {
	category_node_parser_start_element,
	category_node_parser_end_element,
	NULL,
	NULL,
	NULL
};

